﻿using System.Collections.Generic;
using UnityEngine;

namespace Core
{
    public static partial class Utils
    {
        public static class Math
        {
            public static bool Within(long v, long min, long max)
            {
                return v >= min && v <= max;
            }

            public static bool Within(float v, float min, float max)
            {
                return v >= min && v <= max;
            }

            /// <summary>
            /// 取余
            /// </summary>
            /// <param name="value"></param>
            /// <param name="limit"></param>
            /// <returns></returns>
            public static int GetMod(int value, int limit)
            {
                if (value >= 0) return value % limit;

                value %= limit;

                if (value < 0)
                {
                    value += limit;
                }

                return value;
            }

            /// <summary>
            /// 取整
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            // ReSharper disable once MemberCanBePrivate.Global
            public static int GetInt(float value)
            {
                return value >= 0 ? (int)value : (int)value - 1;
            }

            /// <summary>
            /// 取余
            /// </summary>
            /// <param name="value"></param>
            /// <param name="limit"></param>
            /// <returns></returns>
            // ReSharper disable once MemberCanBePrivate.Global
            public static Vector2Int GetMod(Vector2Int value, int limit)
            {
                value.x = GetMod(value.x, limit);
                value.y = GetMod(value.y, limit);
                return value;
            }

            /// <summary>
            /// 取整
            /// </summary>
            /// <param name="value"></param>
            /// <returns></returns>
            public static Vector2Int GetInt(Vector2 value)
            {
                return new Vector2Int(GetInt(value.x), GetInt(value.y));
            }

            /// <summary>
            /// 欧几里得距离 Euclidean Distance
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public static float GetEd(Vector2Int from, Vector2Int to)
            {
                return (from - to).magnitude;
            }

            /// <summary>
            /// 欧几里得距离的平方 Squared Euclidean Distance
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public static float GetSed(Vector2Int from, Vector2Int to)
            {
                return (from - to).sqrMagnitude;
            }

            /// <summary>
            /// 曼哈顿距离 Manhattan Dis
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public static int GetMd(Vector2Int from, Vector2Int to)
            {
                return Mathf.Abs(from.x - to.x) + Mathf.Abs(from.y - to.y);
            }

            /// <summary>
            /// 近似直线距离 Approx Dis
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public static float GetAD(Vector2 from, Vector2 to)
            {
                var distX = Mathf.Abs(from.x - to.x);
                var distZ = Mathf.Abs(from.y - to.y);

                if (distX > distZ)
                {
                    return 1.414f * distZ + (distX - distZ);
                }

                return 1.414f * distX + (distZ - distX);
            }

            /// <summary>
            /// 返回的值范围[0,360)
            /// </summary>
            /// <param name="from"></param>
            /// <param name="to"></param>
            /// <returns></returns>
            public static float GetAngle(Vector3 from, Vector3 to)
            {
                //Vector3.SignedAngle()

                var v3 = Vector3.Cross(from, to); //叉乘判断正方向
                var angle = Vector3.Angle(from, to);
                if (v3.z < 0)
                {
                    angle = 360 - angle;
                }

                return angle;
            }

            /// <summary>
            /// 计算二维方阵行列式的值，该矩阵的行由指定的二维向量组成。
            /// </summary>
            /// <param name="a"></param>
            /// <param name="b"></param>
            /// <returns></returns>
            public static float GetDet(Vector2 a, Vector2 b)
            {
                return a.x * b.y - a.y * b.x;
            }

            /// <summary>
            /// 计算从指定点到指定线段的平方距离。
            /// </summary>
            /// <param name="v1">线段的第一个点</param>
            /// <param name="v2">线段的第二个点</param>
            /// <param name="point">要计算平方距离的点</param>
            /// <returns>点到线段的平方距离</returns>
            public static float GetSDPointToLineSegment(Vector2 v1, Vector2 v2, Vector2 point)
            {
                var r = Vector2.Dot(point - v1, v2 - v1) / (v2 - v1).sqrMagnitude;

                return r switch
                {
                    < 0.0f => (point - v1).sqrMagnitude,
                    > 1.0f => (point - v2).sqrMagnitude,
                    _ => (point - (v1 + r * (v2 - v1))).sqrMagnitude
                };
            }

            /// <summary>
            /// 三点是否共线
            /// </summary>
            /// <param name="p1"></param>
            /// <param name="p2"></param>
            /// <param name="p3"></param>
            /// <returns></returns>
            public static bool IsCollinear1(Vector2 p1, Vector2 p2, Vector2 p3)
            {
                if (Mathf.Approximately(p1.x, p2.x) && Mathf.Approximately(p2.x, p3.x))
                {
                    return true;
                }

                return Mathf.Approximately(p1.y, p2.y) && Mathf.Approximately(p2.y, p3.y);
            }

            /// <summary>
            /// 三点是否共线
            /// </summary>
            /// <param name="p1"></param>
            /// <param name="p2"></param>
            /// <param name="p3"></param>
            /// <returns></returns>
            public static bool IsCollinear(Vector2 p1, Vector2 p2, Vector2 p3)
            {
                var s = (p3.y - p1.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p3.x - p1.x);
                //return s == 0;
                return Mathf.Abs(s) < 1e-6f;
            }

            /// <summary>
            /// 三点是否共线
            /// </summary>
            /// <param name="p1"></param>
            /// <param name="p2"></param>
            /// <param name="p3"></param>
            public static bool IsCollinear(Vector3 p1, Vector3 p2, Vector3 p3)
            {
                //海伦公式
                var edgeA = (p1 - p2).magnitude;
                var edgeB = (p2 - p3).magnitude;
                var edgeC = (p3 - p1).magnitude;

                var p = 0.5f * (edgeA + edgeB + edgeC);
                var s = p * (p - edgeA) * (p - edgeB) * (p - edgeC);
                return !(s > 0);
            }

            /// <summary>
            /// 判断点在多边形内部
            /// </summary>
            /// <param name="polygons"></param>
            /// <param name="vt"></param>
            /// <returns></returns>
            public static bool IsInPolygon(Vector3[] polygons, Vector3 vt)
            {
                if (polygons == null || polygons.Length == 0)
                {
                    return false;
                }

                var oddNodes = false;

                for (int i = 0, j = polygons.Length - 1; i < polygons.Length; ++i)
                {
                    if ((polygons[i].z < vt.z && polygons[j].z >= vt.z ||
                         polygons[j].z < vt.z && polygons[i].z >= vt.z) &&
                        (polygons[i].x <= vt.x || polygons[j].x <= vt.x))
                    {
                        oddNodes ^= (polygons[i].x + (vt.z - polygons[i].z)
                            / (polygons[j].z - polygons[i].z) * (polygons[j].x - polygons[i].x) < vt.x);
                    }

                    j = i;
                }

                return oddNodes;
            }

            /// <summary>
            /// 从Points中找到距离P点最近的点
            /// </summary>
            /// <param name="p"></param>
            /// <param name="points"></param>
            /// <returns></returns>
            public static Vector3 FindNearest(IList<Vector3> points, Vector3 p)
            {
                var minDisSqr = float.MaxValue;
                var result = Vector3.zero;

                foreach (Vector3 point in points)
                {
                    var disSqr = (p - point).sqrMagnitude;
                    if (disSqr >= minDisSqr) continue;

                    minDisSqr = disSqr;
                    result = point;
                }

                return result;
            }

            public static Vector2Int FindNearest(IList<Vector2Int> points, Vector2Int p)
            {
                var minDisSqr = float.MaxValue;
                var result = Vector2Int.zero;

                foreach (Vector2Int point in points)
                {
                    var disSqr = (p - point).sqrMagnitude;
                    if (disSqr >= minDisSqr) continue;

                    minDisSqr = disSqr;
                    result = point;
                }

                return result;
            }

            public static Vector2Int FindArriveCoord1(Vector2Int coord, Vector2Int size, Vector2 unitPos)
            {
                var minDis = float.MaxValue;
                var tgtCoord = coord;

                for (var i = 0; i < size.x; i++)
                {
                    var c = coord.Plus(i, 0);
                    var dis = GetAD(unitPos, c);
                    if (dis >= minDis) continue;

                    tgtCoord = c;
                    minDis = dis;
                }

                return tgtCoord;
            }

            public static Vector2Int FindArriveCoord2(Vector2Int coord, Vector2Int size, Vector2 unitPos)
            {
                var minDis = float.MaxValue;
                var tgtCoord = coord;

                for (var i = 0; i < size.x; i++)
                {
                    for (var j = 0; j < size.y; j++)
                    {
                        if (i != 0 && j != 0 && i != size.x - 1 && j != size.y - 1) continue;

                        var c = coord.Plus(i, j);
                        var dis = GetAD(unitPos, c);
                        if (dis >= minDis) continue;

                        tgtCoord = c;
                        minDis = dis;
                    }
                }

                return tgtCoord;
            }

            public static float GetDisPointToLine(Vector3 pos, Vector3 line1, Vector3 line2)
            {
                const float delta = 0.0001f;

                var a = Vector3.Distance(line1, line2);
                var b = Vector3.Distance(pos, line1);
                var c = Vector3.Distance(pos, line2);

                if (b <= delta) return 0;
                if (a <= delta) return b;

                if (c * c >= a * a + b * b) return b;
                if (b * b >= a * a + c * c) return c;

                var p = (a + b + c) / 2;
                var s = Mathf.Sqrt(p * (p - a) * (p - b) * (p - c));

                return 2 * s / a;
            }
        }
    }
}
